package com.zzyl.nursing.service.impl;

import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;

import ch.qos.logback.core.util.TimeUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.DateUtils;
import com.zzyl.nursing.config.WebSocketServer;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.mapper.DeviceMapper;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.vo.AlertNotifyVo;
import com.zzyl.nursing.vo.DeviceInfo;
import com.zzyl.system.mapper.SysUserRoleMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.AlertRuleMapper;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.service.IAlertRuleService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 报警规则Service业务层处理
 * 
 * @author laoYe
 * @date 2025-06-03
 */
@Service
@Slf4j
public class AlertRuleServiceImpl extends ServiceImpl<AlertRuleMapper, AlertRule> implements IAlertRuleService
{
    @Autowired
    private AlertRuleMapper alertRuleMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    @Autowired
    private SysUserRoleMapper sysUserRoleMapper;

    @Autowired
    private WebSocketServer webSocketServer;

    /**
     * 查询报警规则
     * 
     * @param id 报警规则主键
     * @return 报警规则
     */
    @Override
    public AlertRule selectAlertRuleById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询报警规则列表
     * 
     * @param alertRule 报警规则
     * @return 报警规则
     */
    @Override
    public List<AlertRule> selectAlertRuleList(AlertRule alertRule)
    {
        return alertRuleMapper.selectAlertRuleList(alertRule);
    }

    /**
     * 新增报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int insertAlertRule(AlertRule alertRule)
    {
        return save(alertRule) ? 1 : 0;
    }

    /**
     * 修改报警规则
     * 
     * @param alertRule 报警规则
     * @return 结果
     */
    @Override
    public int updateAlertRule(AlertRule alertRule)
    {
        return updateById(alertRule) ? 1 : 0;
    }

    /**
     * 批量删除报警规则
     * 
     * @param ids 需要删除的报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除报警规则信息
     * 
     * @param id 报警规则主键
     * @return 结果
     */
    @Override
    public int deleteAlertRuleById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    /**
     * 数据告警
     */
    @Override
    public void alertFilter() {
        log.info("数据告警处理.......");
        //查询所遥规则,有效的才处理
        List<AlertRule> alertRules = lambdaQuery().eq(AlertRule::getStatus, 1).list();
        //判断规则是否为空
        if(CollUtil.isEmpty(alertRules)){
            //结束
            log.info("没有规则信息，不处理!");
            return;
        }
        //查询所有设备上报的数据
        List<Object> jsonStringList = stringRedisTemplate.opsForHash().values(CacheConstants.IOT_DEVICE_LAST_DATA);
        if(CollUtil.isEmpty(jsonStringList)){
            log.info("没有最新设备数据");
            return;
        }
        //数据格式化标准化处理，把json字符串转成java对象
        // 所有的数据列表, 所有设备的数据
        List<DeviceData> allList = new ArrayList<>();
        jsonStringList.stream().forEach(jsonString -> {
            //一个设备的数据
            List<DeviceData> dataList = JSONUtil.toList(jsonString.toString(), DeviceData.class);
            allList.addAll(dataList);
        });

        // 核心逻辑处理
        allList.forEach(this::alertFilter);

    }

    /**
     * 数据告警核心逻辑
     * @param data
     */
    private void alertFilter(DeviceData data){
        //判断当前上报的数据是否超过了1分钟。
        LocalDateTime alarmTime = data.getAlarmTime();
        //转换时区
        //alarmTime = alarmTime.atZone(ZoneId.from(ZoneOffset.UTC)).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime();
        // 两个时间的差值
        long seconds = LocalDateTimeUtil.between(alarmTime, LocalDateTime.now(), ChronoUnit.SECONDS);
        if( seconds > 60){
            log.info("数据超过1分钟，不处理!");
            return;
        }
        //data有什么，functionId, productKey
        //查询物模型下的对应设备的规则
        List<AlertRule> allRules = new ArrayList<>();

        // 规则 是为产品来定义
        List<AlertRule> productRules = lambdaQuery().eq(AlertRule::getFunctionId, data.getFunctionId())
                .eq(AlertRule::getProductKey, data.getProductKey())
                .eq(AlertRule::getIotId, "-1")
                .eq(AlertRule::getStatus, 1).list();
        if(CollUtil.isNotEmpty(productRules)){
            allRules.addAll(productRules);
        }
        //这个设备下的规则, 规则 是不为某个设备来定义
        List<AlertRule> deviceRules = lambdaQuery().eq(AlertRule::getFunctionId, data.getFunctionId())
                .eq(AlertRule::getIotId, data.getIotId())
                .eq(AlertRule::getStatus, 1).list();
        if(CollUtil.isNotEmpty(deviceRules)){
            allRules.addAll(deviceRules);
        }
        if(CollUtil.isEmpty(allRules)){
            log.info("这个设备或这个产品下没有规则，不处理!");
            return;
        }

        allRules.forEach(rule -> deviceDataAlarmHandler(rule, data));
    }

    /**
     * 规则与数据的四重校验
     * @param rule
     * @param data
     */
    private void deviceDataAlarmHandler(AlertRule rule, DeviceData data) {
        //第一层校验：
        //判断生效时间, 规则的生效时间，上报的数据时间
        LocalDateTime alarmTime = data.getAlarmTime();
        //规则的生效时间
        String alertEffectivePeriod = rule.getAlertEffectivePeriod();
        //00:00:00~23:59:59
        String[] periodArr = alertEffectivePeriod.split("~");
        LocalTime startTime = LocalTime.parse(periodArr[0]);
        LocalTime endTime = LocalTime.parse(periodArr[1]);
        //判断是否在生效时间内
        if (alarmTime.toLocalTime().isBefore(startTime) || alarmTime.toLocalTime().isAfter(endTime)) {
            log.info("数据不在生效时间段内，不处理!");
            return;
        }

        //第二层校验：
        // 持续周期计数器的key
        String aggKey=CacheConstants.ALERT_TRIGGER_COUNT_PREFIX + rule.getIotId() + ":" + rule.getId() + ":" + rule.getFunctionId();

        //判断数据阈值, 上报的数据值与规则中的值对比
        Double dataValue = Double.valueOf(data.getDataValue());
        Double value = rule.getValue();
        int result = NumberUtil.compare(dataValue, value);
        // 应该用> = <。通过规则的运算符
        String operator = rule.getOperator();
        if((">=".equals(operator) && (result == 1 || result == 0)) || ("<".equals(operator) && result == -1)){
               log.info("数据超出阈值，需要处理!");
        }else{
            log.info("没有达到阈值，不用处理, 持续周期计数清零");
            //持续周期计数清零
            stringRedisTemplate.delete(aggKey);
            return;
        }
        //需要处理

        //第三层校验：
        String silentKey = CacheConstants.ALERT_SILENT_PREFIX + rule.getIotId() + ":" + rule.getId() + ":" + rule.getFunctionId();
        //判断沉默周期, 查询redis中是否存在沉默周期的key exists
        if (stringRedisTemplate.hasKey(silentKey)) {
            log.info("数据在沉默周期内，不处理!");
            return;
        }
        // 不在沉默周期内
        //第四层校验：
        //判断 持续周期 规则中有持续周期
        //告警次数<持续周期 不处理，告警次数+1, 使用redis自增长, string incr, zset zincryb, hash hincrby
        // redis的自增长有什么特点：每次+1，返回最新的值

        long count = stringRedisTemplate.opsForValue().increment(aggKey);
        if(count < rule.getDuration()){
            //没达至持续周期次数不处理
            log.info("没达至持续周期次数不处理: {},{}", aggKey,count);
            return;
        }

        //校验全部通过，
        //当前数据触发报警
        //持续周期计数清零
        stringRedisTemplate.delete(aggKey);
        //添加沉默周期, 单位是分钟，规则
        stringRedisTemplate.opsForValue().set(silentKey, "", rule.getAlertSilentPeriod(), TimeUnit.MINUTES);

        //保持告警通知
        saveAlertData(rule, data);

    }

    @Value("${alert.deviceMaintainerRole}")//修改工的角色
    private String deviceMaintainerRole;

    @Value("${alert.managerRole}")//管理员的角色
    private String managerRole;

    private void saveAlertData(AlertRule rule, DeviceData data) {
        //是否是老人数据, 0:老，1：设备
        Integer alertDataType = rule.getAlertDataType();
        // 所有要通知的人员id
        Set<Long> userIds = new HashSet<>();
        if(alertDataType == 0){
            // 老人
            // 是否是固定设备
            if(data.getLocationType() == 0){
                // 随身设备, 查询护理人员
                List<Long> nursingIds = deviceMapper.selectNursingIdsByIotIdWithElder(data.getIotId());
                userIds.addAll(nursingIds);
            }else{
                //固定设备, 床位
                List<Long> nursingIds = deviceMapper.selectNursingIdsByIotIdWithBed(data.getIotId());
                userIds.addAll(nursingIds);
            }
        }else{
            //设备, 通知修理工, 通过角色名称来查询
            List<Long> maintainerUserIds = sysUserRoleMapper.selectUserIdByRoleName(deviceMaintainerRole);
            userIds.addAll(maintainerUserIds);
        }
        //查询超级管理员
        List<Long> managerUserIds = sysUserRoleMapper.selectUserIdByRoleName(managerRole);
        userIds.addAll(managerUserIds);
        // 批量保存告警数据
        batchInsertAlertData(userIds, rule, data);

        //发送告警通知
        AlertNotifyVo alertNotifyVo = AlertNotifyVo.builder()
                .id(data.getId())
                .accessLocation(data.getAccessLocation())
                .locationType(data.getLocationType())
                .physicalLocationType(data.getPhysicalLocationType())
                .deviceDescription(data.getDeviceDescription())
                .productName(data.getProductName())
                .functionName(data.getFunctionId())
                .dataValue(data.getDataValue())
                .alertDataType(rule.getAlertDataType())
                .build();
        webSocketServer.sendMessageToConsumer(alertNotifyVo, userIds);
    }

    @Autowired
    private IAlertDataService alertDataService;

    /**
     * 批量保存告警数据
     * @param userIds
     * @param rule
     * @param data
     */
    private void batchInsertAlertData(Set<Long> userIds, AlertRule rule, DeviceData data) {
        AlertData alertData = BeanUtil.toBean(data, AlertData.class);
        alertData.setId(null);//让主键自增
        alertData.setAlertRuleId(rule.getId());
        alertData.setType(rule.getAlertDataType());
        alertData.setStatus(0);//待处理
        // 告警的原因
        alertData.setAlertReason(rule.getFunctionId() + rule.getOperator() + rule.getValue() + " 持续了" + rule.getDuration() + "周期，告警了!");
        //有多少个人就创建多少个数据
        List<AlertData> alertDataList = userIds.stream().map(userId -> {
            AlertData pojo = BeanUtil.toBean(alertData, AlertData.class);
            pojo.setUserId(userId);
            pojo.setUpdateTime(new Date());
            return pojo;
        }).collect(Collectors.toList());
        // 批量保存
        alertDataService.saveBatch(alertDataList);
    }


    public static void main(String[] args) {
        /*LocalDateTime date = LocalDateTimeUtil.parse("2025-06-05 16:00:00", DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
        LocalDateTime now = LocalDateTime.now();
        long seconds = LocalDateTimeUtil.between(date, now, ChronoUnit.SECONDS);
        System.out.println("seconds = " + seconds);*/
        // compare的比较结果
        System.out.println(NumberUtil.compare(10, 1));
        System.out.println(NumberUtil.compare(1, 10));
        System.out.println(NumberUtil.compare(10, 10));

    }

}
